﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Xml.XPath;
using System.Xml.Serialization;
using System.Xml;

namespace Vbyte.Configuration
{
    /// <summary>
    /// xml序列化配置分组
    /// </summary>
    [Serializable]
    [XmlRoot(ElementName = "XmlSerializeGroup")]
    public class XmlSerializeSectionGroup : IConfigurationSectionHandler
    {

        #region IConfigurationSectionHandler 成员
        /// <summary>
        /// 创建配置节处理程序。
        /// </summary>
        /// <param name="parent">父对象。</param>
        /// <param name="configContext">配置上下文对象。</param>
        /// <param name="section">节 XML 节点。</param>
        /// <returns>创建的节处理程序对象。</returns>
        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            //XPathNavigator navigator = section.CreateNavigator();
            //string typeName = (string)navigator.Evaluate("string(@type)");
            //Type type = Type.GetType(typeName, true);
            //XmlSerializer serializer = new XmlSerializer(type);
            //return serializer.Deserialize(new XmlNodeReader(section));

            XmlSerializer serializer = new XmlSerializer(this.GetType());
            return serializer.Deserialize(new XmlNodeReader(section));
        }

        #endregion

        private static XmlSerializeSectionGroup _instance = null;
        private static readonly object objLock = new object();

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The instance.</value>
        public static XmlSerializeSectionGroup Instance
        {
            get
            {
                lock (objLock)
                {
                    if (_instance == null)
                    {
                        object sectionObj = System.Configuration.ConfigurationManager.GetSection("XmlSerializeGroup");
                        if (sectionObj != null)
                        {
                            _instance = (XmlSerializeSectionGroup)sectionObj;
                        }
                        else
                        {
                            _instance = new XmlSerializeSectionGroup();
                        }
                    }
                }
                return _instance;
            }
        }

        private XmlSerializeConfig[] _items = new XmlSerializeConfig[0];

        //[XmlArray(ElementName = "ConfigItems"), XmlArrayItem(ElementName = "config")]
        [XmlElement(ElementName = "add")]
        public XmlSerializeConfig[] ConfigItems
        {
            get { return _items; }
            set { _items = value; }
        }

        /// <summary>
        /// 获取节点实例
        /// </summary>
        /// <typeparam name="T">节点类型</typeparam>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public T GetObject<T>(string id)
        {
            T result = default(T);
            for (int i = 0, j = ConfigItems.Length; i < j; i++)
            {
                if (ConfigItems[i].ID.Equals(id, StringComparison.InvariantCulture))
                {
                    //Console.WriteLine("Got:" + ConfigItems[i].ID);
                    result = (T)GetConfigInstance(ConfigItems[i]);
                    break;
                }
            }
            return result;
        }

        private static Dictionary<string, object> StaticDict = new Dictionary<string, object>();

        private object GetDeserializedObject(Type targetType, string xmlConfigVal)
        {
            if (string.IsNullOrEmpty(xmlConfigVal))
            {
                throw new System.Configuration.ConfigurationErrorsException("未能获取配置项目的项目属性值配置！");
            }

            object objRet = null;
            XmlSerializer serializer = new XmlSerializer(targetType);
            xmlConfigVal = xmlConfigVal.Trim();
            //if (!xmlConfigVal.StartsWith("<?xml "))
            //{
            //    xmlConfigVal = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" 
            //        + System.Environment.NewLine 
            //        + xmlConfigVal;
            //}

            //Console.WriteLine("----------------------------");
            //Console.WriteLine(xmlConfigVal); 
            //Console.WriteLine("----------------------------");

            using (System.IO.StringReader valReader = new System.IO.StringReader(xmlConfigVal))
            {
                objRet = serializer.Deserialize(valReader);
            }

            return objRet;
        }

        private object GetConfigInstance(XmlSerializeConfig config)
        {
            object objRet = null;
            if (!string.IsNullOrEmpty(config.ConfigSource))
            {
                string tfPath = string.Empty;
                if (config.ConfigSource.IndexOf(':') != -1)
                {
                    tfPath = config.ConfigSource;
                }
                else
                {
                    if (System.Web.HttpContext.Current != null)
                    {
                        tfPath = System.Web.HttpContext.Current.Server.MapPath(config.ConfigSource);
                    }
                    else
                    {
                        tfPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.ConfigSource.Replace('/', '\\').TrimStart('\\'));
                    }
                }
                config.ConfigValue = System.IO.File.ReadAllText(tfPath);
            }

            Type objType = Type.GetType(config.TypeName, false);
            if (objType == null)
            {
                throw new System.Configuration.ConfigurationErrorsException("类型" + config.TypeName + "不能识别！");
            }

            if (config.IsSingleton)
            {
                lock (objLock)
                {
                    string sKey = config.TypeName + "@id=" + config.ID;
                    if (!StaticDict.ContainsKey(sKey))
                    {
                        StaticDict.Add(sKey, GetDeserializedObject(objType, config.ConfigValue));
                    }
                    objRet = StaticDict[sKey];
                }
            }
            else
            {
                objRet = GetDeserializedObject(objType, config.ConfigValue);
            }
            return objRet;
        }

    }

    [Serializable]
    public struct XmlSerializeConfig
    {
        [XmlAttribute(AttributeName = "id")]
        public string ID { get; set; }

        [XmlAttribute(AttributeName = "type")]
        public string TypeName { get; set; }

        [XmlAttribute(AttributeName = "singleton")]
        public bool IsSingleton { get; set; }

        [XmlAttribute(AttributeName = "configSource")]
        public string ConfigSource { get; set; }

        [XmlText]
        public string ConfigValue { get; set; }
    }

}
